#include "lib_sys_proc.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include <unistd.h>
#include <errno.h>
#include <time.h>

long get_cpu_num()
{
    return sysconf(_SC_NPROCESSORS_CONF);
}

// 初始化,分配内存: 返回2组cpuTime_t
sys_cpuTime_t* init_sys_cpuTime()
{
    long cpu_num = get_cpu_num();
    sys_cpuTime_t* sys_cputime_ptr = malloc(2*sizeof(sys_cpuTime_t));
    if (sys_cputime_ptr == NULL)
    {
        perror("malloc sys_cpuTime failed");
        return NULL;
    }
    sys_cputime_ptr[0].cpus = malloc(cpu_num * sizeof(cpuTime_t));
    sys_cputime_ptr[1].cpus = malloc(cpu_num * sizeof(cpuTime_t));
    sys_cputime_ptr[0].cpu_num = cpu_num;
    sys_cputime_ptr[0].read_count = 0;
    sys_cputime_ptr[1].cpu_num = cpu_num;
    sys_cputime_ptr[1].read_count = 0;
    if (get_sys_cpuTime(sys_cputime_ptr) == NULL)
    {
        free_sys_cpuTime(sys_cputime_ptr);
        return NULL;
    }

    return sys_cputime_ptr;
}

// 释放资源
void free_sys_cpuTime(sys_cpuTime_t* sys_cpuTime_ptr)
{
    free(sys_cpuTime_ptr[0].cpus);
    free(sys_cpuTime_ptr[1].cpus);
    free(sys_cpuTime_ptr);
}

// 获取cpu time
sys_cpuTime_t* get_sys_cpuTime(sys_cpuTime_t* sys_cpuTime_ptr)
{
    struct timespec record_timestamp;
    clock_gettime(CLOCK_MONOTONIC, &record_timestamp);
    long cpu_num = sys_cpuTime_ptr->cpu_num;
    const char proc_stat[32] = "/proc/stat";

    sys_cpuTime_t* now_sys_cpuTime_ptr = NULL;
    if (sys_cpuTime_ptr[0].read_count == sys_cpuTime_ptr[1].read_count)
    {
        now_sys_cpuTime_ptr = &sys_cpuTime_ptr[0];
    }
    else if (sys_cpuTime_ptr[0].read_count > sys_cpuTime_ptr[1].read_count)
    {
        now_sys_cpuTime_ptr = &sys_cpuTime_ptr[1];
    }
    else
    {
        fprintf(stderr, "索引出现错误,请检查代码逻辑");
        exit(-1);
    }

    FILE* fp = fopen(proc_stat, "r");
    if (fp == NULL)
    {
        perror("fopen proc_stat failed");
        return NULL;
    }
    char line[256];
    int ret;
    cpuTime_t cpuTime_it;
    // 第一行读取cpu all
    if (fgets(line, sizeof(line), fp) != NULL)
    {
        ret = sscanf(line, "%*s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
            &cpuTime_it.user_time,
            &cpuTime_it.nice_time,
            &cpuTime_it.system_time,
            &cpuTime_it.idle_time,
            &cpuTime_it.iowait_time,
            &cpuTime_it.irq_time,
            &cpuTime_it.softirq_time,
            &cpuTime_it.steal_time,
            &cpuTime_it.guest_time,
            &cpuTime_it.guest_nice_time
        );
        if (ret > 0)
        {
            cpuTime_it.used_time = cpuTime_it.user_time + cpuTime_it.nice_time + cpuTime_it.system_time + cpuTime_it.irq_time
                + cpuTime_it.iowait_time + cpuTime_it.softirq_time + cpuTime_it.steal_time + cpuTime_it.guest_time + cpuTime_it.guest_nice_time;
            cpuTime_it.total_time = cpuTime_it.used_time + cpuTime_it.idle_time;
            cpuTime_it.timestamp = record_timestamp.tv_sec + record_timestamp.tv_nsec / 1000;
            now_sys_cpuTime_ptr->cpu_all = cpuTime_it;
        }
    }

    long i = 0;
    for (; i != cpu_num; i++)
    {
        if (fgets(line, sizeof(line), fp) == NULL) {
            break;
        }
        if (strstr(line, "cpu"))
        {
            ret = sscanf(line, "%*s %ld %ld %ld %ld %ld %ld %ld %ld %ld %ld",
                &cpuTime_it.user_time,
                &cpuTime_it.nice_time,
                &cpuTime_it.system_time,
                &cpuTime_it.idle_time,
                &cpuTime_it.iowait_time,
                &cpuTime_it.irq_time,
                &cpuTime_it.softirq_time,
                &cpuTime_it.steal_time,
                &cpuTime_it.guest_time,
                &cpuTime_it.guest_nice_time
            );
            if (ret > 0)
            {
                cpuTime_it.used_time = cpuTime_it.user_time + cpuTime_it.nice_time + cpuTime_it.system_time + cpuTime_it.irq_time
                    + cpuTime_it.iowait_time + cpuTime_it.softirq_time + cpuTime_it.steal_time + cpuTime_it.guest_time + cpuTime_it.guest_nice_time;
                cpuTime_it.total_time = cpuTime_it.used_time + cpuTime_it.idle_time;
                cpuTime_it.timestamp = record_timestamp.tv_sec + record_timestamp.tv_nsec / 1000;
                now_sys_cpuTime_ptr->cpus[i] = cpuTime_it;
            }
        }
    }
    fclose(fp);
    if (i == cpu_num)
    {
        now_sys_cpuTime_ptr->read_count++;
        return now_sys_cpuTime_ptr;
    }

    return NULL;
}

int cal_cpu_usage(sys_cpuTime_t* sys_cpuTime_prve, sys_cpuTime_t* sys_cpuTime_now)
{
    if (sys_cpuTime_prve == NULL || sys_cpuTime_now == NULL)
    {
        return -1;
    }
    cpuTime_t* cpuTime_last, * cpuTime_now;
    // 总cpu使用率
    cpuTime_last = &sys_cpuTime_prve->cpu_all;
    cpuTime_now = &sys_cpuTime_now->cpu_all;
    cpuTime_now->usage = (cpuTime_now->used_time - cpuTime_last->used_time) / (cpuTime_now->total_time - cpuTime_last->total_time);

    // 单cpu使用率
    for (long i = 0; i < sys_cpuTime_now->cpu_num; i++)
    {
        cpuTime_last = &sys_cpuTime_prve->cpus[i];
        cpuTime_now = &sys_cpuTime_now->cpus[i];
        cpuTime_now->usage = (cpuTime_now->used_time - cpuTime_last->used_time) / (cpuTime_now->total_time - cpuTime_last->total_time);
    }

    return 0;
}

void show_cpu_usage(sys_cpuTime_t* sys_cpuTime_now)
{
    cpuTime_t* cpuTime_now = &sys_cpuTime_now->cpu_all;
    printf("cpu all: %.2lf%%\n", cpuTime_now->usage);
    for (long i = 0; i < sys_cpuTime_now->cpu_num; i++)
    {
        cpuTime_now = &sys_cpuTime_now->cpus[i];
        printf("cpu[%2ld]: %.2lf%%\n", i, cpuTime_now->usage);
    }
    printf("----------------\n");
}